/*
 * hudong.com Inc.
 * Copyright (c) 2000-2004 All Rights Reserved.
 */
package middlegen.plugins.ibatis;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import middlegen.PreferenceAware;
import middlegen.Table;
import middlegen.javax.Sql2Java;
import middlegen.plugins.ibatis.config.OperationConfig;
import middlegen.plugins.ibatis.config.ParamConfig;
import middlegen.plugins.ibatis.util.SqlParser;
import middlegen.plugins.ibatis.util.Util;

import org.apache.commons.lang.StringUtils;

/**
 * A decoration class for a dao operation.
 *
  */
public abstract class IWalletOperation extends PreferenceAware implements Operation {
    public static final String MAPPED_STATEMENT_PREFIX = "MS-";
    public static final String PARAM_TYPE_OBJECT       = "object";
    public static final String PARAM_TYPE_PRIMITIVE    = "primitive";
    public static final String MULTIPLICITY_ONE        = "one";
    public static final String MULTIPLICITY_MANY       = "many";

    /** an operation config instance holds operation configuration. */
    protected OperationConfig  opConfig;

    /** the table instance */
    private Table              table;

    /** a list of all method parameters, each one is an instance of IWalletParameter */
    protected List             objectParams            = new ArrayList();

    /** a list of all method parameters, each one is an instance of IWalletParameter */
    protected List             primitiveParams         = new ArrayList();

    /** the type of how to pass parameters to dao */
    protected String           paramType;

    /** the multiplicity of results */
    protected String           multiplicity;

    /**
     * Constructor for IWalletOperation.
     */
    public IWalletOperation(OperationConfig conf) {
        super();

        opConfig = conf;
    }

    /**
     * @return
     *
     * @see middlegen.PreferenceAware#prefsPrefix()
     */
    protected String prefsPrefix() {
        return "java-method";
    }

    /**
     * @return
     *
     * @see middlegen.plugins.ibatis.Operation#getName()
     */
    public String getName() {
        return opConfig.getName();
    }

    /**
     * @return
     *
     * @see middlegen.plugins.ibatis.Operation#getTable()
     */
    public Table getTable() {
        return table;
    }

    /**
     * @param table
     */
    public void setTable(Table t) {
        table = t;

        setupParams();

        IWalletTable iwTable = (IWalletTable) t;

        if (getParams().size() > 1) {
            // because we need a map to bundle all params.
            iwTable.addIbatisImport("java.util.Map");
            iwTable.addIbatisImport("java.util.HashMap");
        }

        // add imports for return type
        iwTable.addDaoImport(getReturnType());

        // add imports for all exception type
        iwTable.addDaoImports(getExceptions());

        // add imports for ibatis
        iwTable.addIbatisImports(getIbatisExceptions());
    }

    /**
     * @return
     *
     * @see middlegen.plugins.ibatis.Operation#getParsedSql()
     */
    public String getParsedSql() {
        return opConfig.getZst().toString();
    }

    /**
     * @return
     */
    public SqlParser getSqlParser() {
        return opConfig.getSqlParser();
    }

    /**
     * @return
     *
     * @see middlegen.plugins.ibatis.Operation#getDefaultReturnValue()
     */
    public String getDefaultReturnValue() {
        return Util.getDefaultValue(getReturnType());
    }

    /**
     * @return
     */
    public List getParams() {
        if (PARAM_TYPE_OBJECT.equals(paramType)) {
            return objectParams;
        } else {
            // as default;
            return primitiveParams;
        }
    }

    /**
     * @return
     */
    public IWalletParameter getParam() {
        if (getParams().size() == 1) {
            return (IWalletParameter) getParams().get(0);
        } else {
            return null;
        }
    }

    /**
     * @return
     *
     * @see middlegen.plugins.ibatis.Operation#getExceptions()
     */
    public List getExceptions() {
        List list = new ArrayList();

        list.add(IWalletPlugin.DATA_ACCESS_EXCEPTION_CLASS);

        return list;
    }

    /**
     * @return
     *
     * @see middlegen.plugins.ibatis.Operation#getSimpleExceptions()
     */
    public List getSimpleExceptions() {
        List list = getExceptions();
        List newList = new ArrayList();

        for (int i = 0; i < list.size(); i++) {
            String exception = (String) list.get(i);

            newList.add(Util.getSimpleJavaType(exception));
        }

        return newList;
    }

    /**
     * @return
     */
    public String getMappedStatementId() {
        return MAPPED_STATEMENT_PREFIX
               + Util.toUpperCaseWithDash(((IWalletTable) getTable()).getBaseClassName()) + "-"
               + Util.toUpperCaseWithDash(getName());
    }

    /**
     * @param origSql
     * @return
     */
    protected String getMappedStatementSql(String origSql) {
        if (StringUtils.isBlank(origSql)) {
            return origSql;
        }

        StringBuffer msSql = new StringBuffer();

        int startIndex = 0;
        int endIndex = origSql.indexOf("?");
        Iterator iParams = primitiveParams.iterator();

        while ((endIndex >= 0) && iParams.hasNext()) {
            msSql.append(origSql.substring(startIndex, endIndex));

            IWalletParameter param = (IWalletParameter) iParams.next();
            String paramName;

            if (PARAM_TYPE_PRIMITIVE.equals(paramType) && (primitiveParams.size() == 1)) {
                paramName = "value";
            } else {
                paramName = param.getName();
            }

            // deal with Money mapping 
            if ("Money".equals(param.getSimpleJavaType())) {
                paramName = paramName + ".cent";
            }

            //            // add by zhaoxu 2007-04-11 -->>>
            //            String sqlType = param.getSqlType();
            //            if ("VARCHAR2".equals(sqlType)) {
            //                sqlType = "VARCHAR";
            //            } else if (sqlType.indexOf("(") > 0) {
            //                sqlType = sqlType.substring(0, sqlType.indexOf("("));
            //            }
            //            
            //            if (sqlType != null && sqlType.length() > 0) {
            //            	paramName = paramName + ":" + sqlType;
            //            }
            //            // add by zhaoxu 2007-04-11 --<<<

            msSql.append("#").append(paramName).append("#");

            startIndex = endIndex + 1;
            endIndex = origSql.indexOf("?", startIndex);
        }

        msSql.append(origSql.substring(startIndex));

        String append = opConfig.getAppend();
        if (StringUtils.isNotBlank(append)) {
            msSql.append(" ").append(append);
        }

        return msSql.toString();
    }

    /**
     * Replace all parameter placeholders in parsedSql with their corresponding names.
     *
     * @return
     */
    public String getMappedStatementSql() {
        if (opConfig.isHasSqlmap()) {
            // TODO: optimize the logic
            List params = getParams();
            List paramNames = new ArrayList();

            for (int i = 0; i < params.size(); i++) {
                paramNames.add(((IWalletParameter) params.get(i)).getName());
            }

            return opConfig.getSqlmap(paramNames);
        } else {
            return getMappedStatementSql(getParsedSql());
        }
    }

    /**
     * @return
     */
    public String getParamType() {
        return paramType;
    }

    /**
     * @return
     */
    public String getMultiplicity() {
        return multiplicity;
    }

    /**
     */
    protected void setupParams() {
        IWalletTable iwTable = (IWalletTable) getTable();

        IWalletParameter param;

        param = new IWalletParameter(this, iwTable.getSingularisedVariableName());

        param.setPlugin(getPlugin());
        objectParams.add(param);

        if (PARAM_TYPE_OBJECT.equals(paramType)) {
            iwTable.addDaoImport(param.getQualifiedJavaType());
        }

        List rawParams = getSqlParser().getParams();
        Map usedNames = new HashMap();

        for (Iterator i = rawParams.iterator(); i.hasNext();) {
            String paramName = (String) i.next();

            if (!usedNames.containsKey(paramName)) {
                param = new IWalletParameter(this, paramName);
                usedNames.put(paramName, new Integer(1));
            } else {
                int suffix = ((Integer) usedNames.get(paramName)).intValue();

                suffix++;
                param = new IWalletParameter(this, paramName, String.valueOf(suffix));
                usedNames.put(paramName, new Integer(suffix));
            }

            // add by zhaoxu 2007-04-11 -->>>
            String sqlType = null;
            try {
                sqlType = iwTable.getColumn(paramName).getSqlTypeName();
                param.setSqlType(sqlType);
            } catch (Exception e) {
                ;
            }
            // add by zhaoxu 2007-04-11 --<<<
            param.setPlugin(getPlugin());
            primitiveParams.add(param);

            if (PARAM_TYPE_PRIMITIVE.equals(paramType)) {
                iwTable.addDaoImport(param.getQualifiedJavaType());
            }
        }

        // add additional parameters needed by extraparams
        if (!opConfig.getExtraParams().isEmpty()) {
            for (Iterator i = opConfig.getExtraParams().iterator(); i.hasNext();) {
                ParamConfig paramConfig = (ParamConfig) i.next();

                param = new IWalletParameter(this, paramConfig.getName());
                param.setJavaType(paramConfig.getJavaType());

                param.setPlugin(getPlugin());

                if (PARAM_TYPE_OBJECT.equals(paramType)) {
                    objectParams.add(param);
                } else {
                    primitiveParams.add(param);
                }

                iwTable.addDaoImport(param.getQualifiedJavaType());
                iwTable.addIbatisImport(param.getQualifiedJavaType());
            }
        }

        // add additional parameters needed by paging
        if (isPaging()) {
            param = new IWalletParameter(this, IWalletParameter.PARAM_NAME_PAGE_SIZE);

            param.setPlugin(getPlugin());

            if (PARAM_TYPE_OBJECT.equals(paramType)) {
                objectParams.add(param);
            } else {
                primitiveParams.add(param);
            }

            param = new IWalletParameter(this, IWalletParameter.PARAM_NAME_PAGE_NUM);
            param.setPlugin(getPlugin());

            if (PARAM_TYPE_OBJECT.equals(paramType)) {
                objectParams.add(param);
            } else {
                primitiveParams.add(param);
            }
        }
    }

    /**
     * @return
     */
    public List getIbatisExceptions() {
        List list = new ArrayList();

        return list;
    }

    /**
     *
     * @return
     */
    public boolean isReturnTypePrimitive() {
        // simplistic approach
        return Sql2Java.isPrimitive(getReturnType());
    }

    /**
     * @return
     */
    public String getReturnTypeForPrimitive() {
        return Sql2Java.getClassForPrimitive(getReturnType());
    }

    /**
     * @return
     */
    public String getSimpleReturnTypeForPrimitive() {
        return Util.getSimpleJavaType(getReturnTypeForPrimitive());
    }

    /**
     * @return
     */
    public boolean isPaging() {
        return false;
    }

    /**
     * @return
     */
    public boolean isHasSqlmap() {
        return opConfig.isHasSqlmap();
    }

    //<add author="jdyao" time="2009-08-24">
    public boolean isSequence() {
        return false;
    }
    //</add>
}
